//
//  MobileTracking.m
//  MobileTracking
//
//  Created by Wenqi on 14-3-11.
//  Copyright (c) 2014年 Admaster. All rights reserved.
//


#import "MobileTracking.h"
#import "MMA_Macro.h"
#import "MMA_SDKConfig.h"
#import "MMA_XMLReader.h"
#import "MMA_Log.h"
#import "MMA_Task.h"
#import "MMA_TaskQueue.h"
#import "MMA_Helper.h"

#import "LocationService.h"
#import "TrackingInfoService.h"
#import "EncryptModule.h"
#import "RequestQueue.h"
#import "GTMNSString+URLArguments.h"


@interface MobileTracking()

@property (atomic, strong) MMA_SDKConfig *sdkConfig;
@property (nonatomic, strong) NSString *sdkConfigURL;
@property (nonatomic, strong) MMA_TaskQueue *sendQueue;
@property (nonatomic, strong) MMA_TaskQueue *failedQueue;
@property (nonatomic, strong) TrackingInfoService *trackingInfoService;
@property (nonatomic, strong) NSTimer *failedQueueTimer;
@property (nonatomic, strong) NSTimer *sendQueueTimer;
@property (nonatomic, assign) BOOL isTrackLocation;

@end

@implementation MobileTracking

+ (MobileTracking *)sharedInstance {
    static MobileTracking *_sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

- (instancetype)init
{
    if (self = [super init]) {
        
        _trackingInfoService = [TrackingInfoService sharedInstance];
        _isTrackLocation = false;
        
        [self initSdkConfig];
        [self initQueue];
        [self initTimer];
        [self openLBS];
        
    }
    return self;
}

- (void)initSdkConfig
{
    NSData *sdkData = [[NSUserDefaults standardUserDefaults]  dataForKey:SDK_CONFIG_DATA_KEY];
    if (sdkData) {
        _sdkConfig = [MMA_XMLReader sdkConfigWithData:sdkData];
    } else {
        NSString *localSdkFilePath = [[NSBundle mainBundle] pathForResource:SDK_CONFIG_FILE_NAME ofType:SDK_CONFIG_FILE_EXT];
        NSData *localSdkData = [[NSData alloc] initWithContentsOfFile:localSdkFilePath];
        _sdkConfig = [MMA_XMLReader sdkConfigWithData:localSdkData];
    }
}

- (void)initQueue
{
    _sendQueue = [[MMA_TaskQueue alloc] initWithIdentity:SEND_QUEUE_IDENTITY];
    _failedQueue = [[MMA_TaskQueue alloc] initWithIdentity:FAILED_QUEUE_IDENTITY];
    
    [_sendQueue loadData];
    [_failedQueue loadData];
    
}

- (void)initTimer
{
    NSInteger failedQueueInterval = self.sdkConfig.offlineCache.queueExpirationSecs;
    
    if (!failedQueueInterval) {
        failedQueueInterval = DEFAULT_FAILED_QUEUE_TIMER_INTERVAL;
    }
    _failedQueueTimer = [NSTimer scheduledTimerWithTimeInterval:failedQueueInterval
                                             target:self
                                           selector:@selector(handleFailedQueueTimer:)
                                           userInfo:nil
                                            repeats:YES];
    
    _sendQueueTimer = [NSTimer scheduledTimerWithTimeInterval:DEFAULT_SEND_QUEUE_TIMER_INTERVAL
                                                         target:self
                                                       selector:@selector(handleSendQueueTimer:)
                                                       userInfo:nil
                                                        repeats:YES];
}

- (void)handleFailedQueueTimer:(NSTimer *)timer
{
    NSInteger netStatus = [self.trackingInfoService networkCondition];
    if (netStatus == NETWORK_STATUS_NO) {
        return;
    }
    while ([self.failedQueue count] > 0) {
        MMA_Task *task = [self.failedQueue pop];
        
        MMA_Company *company = [self confirmCompany:task.url];
        [MMA_Log log:@"##failed_queue_url:%@" withParameters:task.url];
        NSInteger offlineCacheExpiration = company.MMASwitch.offlineCacheExpiration;
        NSInteger now = [[[NSDate alloc] init] timeIntervalSince1970];
        if (task.timePoint + offlineCacheExpiration < now) {
            continue;
        }
        NSURL *URL = [NSURL URLWithString:task.url];
        NSURLCacheStoragePolicy policy = NSURLRequestReloadIgnoringCacheData;
        NSURLRequest *request = [NSURLRequest requestWithURL:URL cachePolicy:policy timeoutInterval:self.sdkConfig.offlineCache.timeout];
        RQOperation *operation = [RQOperation operationWithRequest:request];
        
        operation.completionHandler = ^(__unused NSURLResponse *response, NSData *data, NSError *error)
        {
            if (error) {
                task.failedCount++;
                if (task.failedCount <= FAILED_QUEUE_TRY_SEND_COUNT) {
                    [self.failedQueue push:task];
                }
            }
        };
        [[RequestQueue mainQueue] addOperation:operation];
    }
}

- (void)handleSendQueueTimer:(NSTimer *)timer
{
    NSInteger netStatus = [self.trackingInfoService networkCondition];
    if (netStatus == NETWORK_STATUS_NO) {
        return;
    }
    if ([self.sendQueue count] >= self.sdkConfig.offlineCache.length) {
        while ([self.sendQueue count] > 0) {
            MMA_Task *task = [self.sendQueue pop];
            [MMA_Log log:@"##send_queue_url:%@" withParameters:task.url];
            NSURL *URL = [NSURL URLWithString:task.url];
            NSURLCacheStoragePolicy policy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
            NSURLRequest *request = [NSURLRequest requestWithURL:URL cachePolicy:policy timeoutInterval:self.sdkConfig.offlineCache.timeout];
            RQOperation *operation = [RQOperation operationWithRequest:request];
            
            operation.completionHandler = ^(__unused NSURLResponse *response, NSData *data, NSError *error)
            {
                if (error && error.domain != NSURLErrorDomain) {
                    task.failedCount++;
                    task.hasFailed = true;
                    [self.failedQueue push:task];
                }
            };
            [[RequestQueue mainQueue] addOperation:operation];
        }
    }
}

- (void)didEnterBackground
{
    if (self.sendQueueTimer) {
        [self.sendQueueTimer invalidate];
        self.sendQueueTimer = nil;
    }
    
    if (self.failedQueueTimer) {
        [self.failedQueueTimer invalidate];
        self.failedQueueTimer = nil;
    }
    
    [self.sendQueue persistData];
    [self.failedQueue persistData];
    
}

- (void)willTerminate
{
    [self didEnterBackground];
}

- (void)didEnterForeground
{
    [self initSdkConfig];
    [self initQueue];
    [self initTimer];
    [self openLBS];
}

- (void)updateSdkConfig
{
    @try {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:weakSelf.sdkConfigURL]];
            MMA_SDKConfig *sdkConfig = [MMA_XMLReader sdkConfigWithData:data];
            if (sdkConfig && [sdkConfig.companies count]) {
                [[NSUserDefaults standardUserDefaults] setValue:data forKey:SDK_CONFIG_DATA_KEY];
                [[NSUserDefaults standardUserDefaults] setInteger:time(NULL) forKey:SDK_CONFIG_LAST_UPDATE_KEY];
                [[NSUserDefaults standardUserDefaults] synchronize];
            }
        });
    }
    @catch (NSException *exception) {
        [MMA_Log log:@"##updateSdkConfig exception:%@" withParameters:exception];
    }

}

- (void)configFromUrl:(NSString *)url
{
    @try {
        self.sdkConfigURL = url;
        
        NSInteger now = time(NULL);
        NSInteger sdkLastUpdateTime = [[NSUserDefaults standardUserDefaults] integerForKey:SDK_CONFIG_LAST_UPDATE_KEY];
        NSInteger netStatus = [self.trackingInfoService networkCondition];
        
        if (netStatus == NETWORK_STATUS_3G && now - sdkLastUpdateTime > UPDATE_SDK_CONFIG_3G_INTERVAL) {
            [self updateSdkConfig];
        } else if (netStatus == NETWORK_STATUS_WIFI && now - sdkLastUpdateTime > UPDATE_SDK_CONFIG_WIFI_INTERVAL) {
            [self updateSdkConfig];
        }
    }
    @catch (NSException *exception) {
        [MMA_Log log:@"##configFromURL exception:%@" withParameters:exception];
    }
}


- (void)enableLog:(BOOL)enableLog {
    [MMA_Log setDebug:enableLog];
}

- (BOOL)clearAll
{
    [self.failedQueue clear];
    [self.sendQueue clear];
    return YES;
}

- (BOOL)clearErrorList
{
    [self.failedQueue clear];
    return YES;
}

- (void)openLBS
{
    [self.sdkConfig.companies enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        MMA_Company *company = (MMA_Company *)obj;
        if (company.MMASwitch.isTrackLocation) {
            [LocationService sharedInstance];
            self.isTrackLocation = true;
        }
    }];
    
}

- (void)click:(NSString *)url
{
    [self view:url];
}

- (void)view:(NSString *)url
{
    @try {
        [self filterURL:url];
    }
    @catch (NSException *exception) {
        [MMA_Log log:@"##exception:%@" withParameters:exception];
    }
}

- (void)filterURL:(NSString *)url
{
    if ([self confirmCompany:url] == nil) {
        return;
    }
    
    [self pushTask:url];
}

- (MMA_Company *)confirmCompany:(NSString *)url
{
    MMA_Company *company = nil;
    NSString *host = [[NSURL URLWithString:url] host];
    for (MMA_Company *__company in [self.sdkConfig.companies objectEnumerator]) {
        for (NSString *domain in __company.domain) {
            if ([host rangeOfString:domain].length > 0) {
                company = __company;
                break;
            }
        }
    }
    return company;
}


- (void)pushTask: (NSString *)url
{
    @try {
        NSString *trackURL = [self generateTrackingURL:url];
        MMA_Task *task = [[MMA_Task alloc] init];
        task.url = trackURL;
        task.timePoint = [[[NSDate alloc] init] timeIntervalSince1970];
        task.failedCount = 0;
        task.hasFailed = false;
        task.hasLock = false;
        [self.sendQueue push:task];
    }
    @catch (NSException *exception) {
        [MMA_Log log:@"##pushTask exception:%@" withParameters:exception];
    }
}


- (NSString *)generateTrackingURL: (NSString *)url
{
    
    MMA_Company *company = [self confirmCompany:url] ;
    NSMutableString *trackURL = [NSMutableString stringWithString:url];
    
    for (MMA_Argument *argument in [company.config.arguments objectEnumerator]) {
        NSString *queryArgsKey = [(MMA_Argument *)[company.config.arguments objectForKey:argument.key] value];;
        
        if ([argument.key isEqualToString:TRACKING_KEY_OS]) {
    
            [trackURL appendFormat:@"%@%@%@%d", company.separator, queryArgsKey, company.equalizer, TRACKING_KEY_OS_VALUE];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_MAC] && IOSV < IOS7) {
            
            NSString *macAddress = self.trackingInfoService.macAddress;
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, [MMA_Helper md5HexDigest:macAddress]];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_IDFA] && IOSV >= IOS6) {
            
            NSString *idfa = [[self.trackingInfoService idfa] gtm_stringByEscapingForURLArgument];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, idfa];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_OPENUDID]) {
            
            NSString *openUDID = [[self.trackingInfoService openUDID] gtm_stringByEscapingForURLArgument];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, openUDID];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_TS]) {

            NSString *timestamp = [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970] * 1000];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, timestamp];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_LBS] && self.isTrackLocation) {
            NSString *location = [self.trackingInfoService location];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, location];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_OSVS]) {
            
            NSString *osVersion = [[self.trackingInfoService systemVerstion] gtm_stringByEscapingForURLArgument];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, osVersion];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_TERM]) {
            
            NSString *term = [[self.trackingInfoService term] gtm_stringByEscapingForURLArgument];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, term];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_WIFI]) {
            
            NSInteger netStatus = [self.trackingInfoService networkCondition];
            [trackURL appendFormat:@"%@%@%@%d", company.separator, queryArgsKey, company.equalizer, netStatus];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_SCWH]) {
            
            NSString *scwh = [self.trackingInfoService scwh];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, scwh];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_AKEY]) {
            
            NSString *appKey = [[self.trackingInfoService appKey] gtm_stringByEscapingForURLArgument];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, appKey];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_ANAME]) {
            
            NSString *appName = [[self.trackingInfoService appName] gtm_stringByEscapingForURLArgument];
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, appName];
            
        } else if ([argument.key isEqualToString:TRACKING_KEY_SDKVS]) {
            
            [trackURL appendFormat:@"%@%@%@%@", company.separator, queryArgsKey, company.equalizer, MMA_SDK_VERSION];
        }
    }
    
    // 添加签名加密模块
    NSString *signString = [[EncryptModule sharedEncryptModule] signUrl:trackURL];
    [MMA_Log log:@"signString: %@"  withParameters:signString];
    [trackURL appendFormat:@"%@%@%@%@", company.separator, company.signature.paramKey, company.equalizer, signString];
    [MMA_Log log:@"trackURL: %@"  withParameters:trackURL];
    
    return trackURL;
}

@end
